package com.lyf.scm.admin.domain.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyf.scm.common.enums.PlanRedisCacheEnum;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * spring -- redis缓存配置
 *
 * @author wangchlei
 * @date 2020/09/19
 */
@Configuration
@EnableCaching
public class RedisConfig {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 缓存配置管理器
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        // 配置序列化（缓存默认有效期 3小时）
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(PlanRedisCacheEnum.DEFAULT_CACHE.getExpireDuration()));
        RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //自定义配置的超时时间
        Map<String, RedisCacheConfiguration> initialCacheConfiguration = new HashMap<String, RedisCacheConfiguration>() {{
            //设置门店失效时长为10小时
            put(PlanRedisCacheEnum.PLAN_STORE_CACHE.getCacheName(), RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(PlanRedisCacheEnum.PLAN_STORE_CACHE.getExpireDuration())).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
            //设置门店失效时长为10小时
            put(PlanRedisCacheEnum.PLAN_DIRECT_STORE_CACHE.getCacheName(), RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(PlanRedisCacheEnum.PLAN_DIRECT_STORE_CACHE.getExpireDuration())).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
        }};
        RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
                .withInitialCacheConfigurations(initialCacheConfiguration)
                .transactionAware().build();
        return cacheManager;
    }

    /**
     * Set value for key, only if key does not exist.
     *
     * @param key
     * @param value
     * @param seconds 过期时间
     * @return
     */
    public boolean setNX(final String key, final String value, final int seconds) {
        boolean result = stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                RedisSerializer<String> serializer = stringRedisTemplate
                        .getStringSerializer();
                byte[] keyByte = serializer.serialize(key);
                byte[] nameByte = serializer.serialize(value);
                boolean flag = connection.setNX(keyByte, nameByte);
                if (flag) {
                    connection.expire(keyByte, seconds);
                }
                return flag;
            }

        });
        return result;
    }

    /**
     * 获取value
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        String result = (String) stringRedisTemplate.execute(new RedisCallback<String>() {

            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = stringRedisTemplate
                        .getStringSerializer();
                byte[] keyByte = serializer.serialize(key);
                byte[] valueByte = connection.get(keyByte);
                if (valueByte == null) {
                    return null;
                }
                String value = serializer.deserialize(valueByte);
                return value;
            }

        });
        return result;
    }

    /**
     * 获取过期时间
     *
     * @param key
     * @return
     */
    public Long getExpiry(final String key) {
        return stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    public void remove(String key) {
        if (stringRedisTemplate.hasKey(key)) {
            stringRedisTemplate.delete(key);
        }
    }

    public void ceshi() {
        //key,value,过期时间,时间单位 s
        stringRedisTemplate.opsForValue().set("key", "value", 20, TimeUnit.SECONDS);
        //获取对应key的value
        stringRedisTemplate.opsForValue().get("key");
    }


}
